#include <iostream>
using namespace std;


进程间通信
本节重点:
·进程间通信介绍
·管道
·消息队列
·共享内存

信号量
进程间通信介绍
进程间通信目的
·数据传输:一个进程需要将它的数据发送给另一个进程
·资源共享:多个进程之间共享同样的资源。
·通知事件:一个进程需要向另一个或一组进程发送消息，
 通知它(它们)发生了某种事件(如进程终止时要通知父进程)。
·进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程)，
 此时控制进程希望能够拦截另个进程的所有陷入和异常，并能够及时知道它的状态改变。

1.进程为什么要通信?
进程也是需要某种协同的，所以如何协同的前提条件是 ---通信---
数据是有类别的,例如：通知就绪的，单纯就是数据的，控制相关的信息...
进程是具有【独立性】的。 
进程 = 内核数据结构 + 代码和数据 

2.进程是如何通信的？怎么办？
a.进程间通信，成本会稍微高一些，因为它们具有独立性。
b.进程间通信的前提：不同的进程，能获取同一份(操作系统)资源(“一段内存”)

①.一定是某一个进程先需要通信，让OS创建一个共享资源
②. OS必须提供很多的系统调用(OS内部的任何资源要让外部使用)
(OS创建的共享资源的不同，系统调用接口的不同 -- 进程间通信会有不同的种类!)

3. 进程通信的常见方式是什么？
 < system V >
1.标准       （专利）

2.方式：
a.消息队列
b.共享内存
c.信号量

直接复用内核代码直接通信？
管道 |   (只允许父子间单向通信 -- 简单)
1.命名
2.匿名
管道 | --- 匿名管道

理解一种现象:为什么父子进程会向同一个显示器终端打印数据!
进程默认会打开三个标准输出输出:0,1,2(标准输入、输出、错误) --怎么做到的?
bash的子进程 --- bash打开了，所有的子进程默认也就打开了，我们只要做好约定即可!!
close(); 为什么我们子进程主动close(0/1/2)标准输入、输出、错误，不影响父进程继续使用显示器文件呢?
file -> ref_count--;         //引用计数(内存级)，(父、子指针指向同一块空间，引用计数+1)
if(ref count==0) 引用计数(内存级) == 0，才释放文件资源

父子进程看到的同一块 内存级文件缓冲区 叫做管道文件！（回到 2.b ）

父子读写文件的时候可以关闭不需要的fd(文件描述符),
例如：父(只读)，子(只写)。
父子既然要关闭不需要的fd，为什么要打开呢?
答：为了让子进程继承下去时，拥有权限！
可以不关闭吗?  答：可以的！(建议关了，防止误写。存在文件描述符泄漏)

管道的单独设计：
int pipe(int pipefd[2]);    //底层 open ,（输出型参数）以数组的形式把【文件描述符】带出来
不需要文件路径 和 文件名 ：匿名管道 

1.如果我想双向通信呢？ 创建两个管道
2.为什么要单向通信？   
为了简单(复用内核代码) --> 只让他进行单向通信。 
这个过程类似管道一端进一端出所以命名为：管道

如何使用管道通信呢？ demo -- 测试管道结构


int main()
{
   cout << "hello C++\n"<< endl;

    return 0;
}


//———————————————————————————————————————————————————————————————————


//———————————————————————————————————————————————————————————————————


//           《 Makefile 》

bin = testpipe
src = testpipe.cc

# test:test.c //# 注释 @ ==> test == bin , ^ ==> test.c == src
$(bin) : $(src)
	@g++ -o $@ $^ -std=c++11 
.PHONY:clean
clean :
	@rm - f $(bin)


//———————————————————————————————————————————————————————————————————


//           《 testpipe.cc 》



#include <iostream>
#include <unistd.h>   //vscode 在 windows 环境下无法使用，安装插件Clangd
#include <cerrno>     //errne.h   (C++版)
#include <cstring>    //string.h  (C++版)

// int pipe(int pipefd[2]);  成功返回0，否则返回-1
// pipefd[0] -> r , pipefd[1] -> w ，读==[0]， 写==[1]

int main()
{
    //close(0);       //如果关闭0， 再打开的流 就是从 0 (最小的)开始
    // 1. 创建管道
    int pipefd[2];
    int n = pipe(pipefd);   //输出型参数，rfd, wfd 
    if(n != 0)
    {
        //输出标准错误
        std::cerr << "errno: " << errno << ": " << "errstring: " << strerror(errno) << std::endl;
        return 1;
    }
    //注意：系统默认打开（0,1,2）三个标准流，接下来打开的是 3...
    std::cout << "pipefd[0]: " << pipefd[0] << ", pipefd[1]: " << pipefd[1] << std::endl;

    //2.创建子进程
    pid id = fork();
    if(id == 0)
    {
        //子进程
        exit(0);
    }

    //父进程

    return 0;
}



// 1. 创建管道 测试结果：
// [a@192 pipe]$ ./testpipe 
// pipefd[0]: 3, pipefd[1]: 4

//close(0)； 后
// pipefd[0]: 0, pipefd[1]: 3






